import logging
import sys
from math import degrees
from functools import lru_cache
from itertools import cycle
import xml.etree.ElementTree as ET

import pygame

from .settings import *


class Square:
    def __init__(self, center, width, rotation = 0.0):
        """rotation = 0.0   #radium"""
        self.rotation = rotation
        self.center = Vector2(center)
        self.width = width

    def rotate_ip(self, delta_rad):
        """change the value, then return it"""
        self.rotation += delta_rad
        return self.rotation

    def move(self, dx: Vector2):
        self.center += dx

    @property
    def rect0(self):
        return pygame.Rect(self.center - Vector2(self.width)/2, Vector2(self.width))

    @property
    def external_rect(self):
        sidelength = max(abs((self.topleft - self.center).x), abs((self.topleft - self.center).y)) * 2
        r = pygame.Rect((0,0), (sidelength, sidelength))
        r.center = self.center
        return r

    @property
    def radius(self):
        return self.width * 0.707105


    @property
    def topleft(self):
        return self.center + Vector2(-self.width/2, -self.width/2).rotate(-degrees(self.rotation))

    @property
    def bottomleft(self):
        return self.center + Vector2(-self.width/2, self.width/2).rotate(-degrees(self.rotation))

    @property
    def topright(self):
        return self.center + Vector2(self.width/2, -self.width/2).rotate(-degrees(self.rotation))

    @property
    def bottomright(self):
        return self.center + Vector2(self.width/2, self.width/2).rotate(-degrees(self.rotation))

    def draw(self):

        pygame.draw.circle(Camera.screen, "green", self.topleft, radius= 2)

        pygame.draw.polygon(Camera.screen, "yellow", [self.topleft, self.topright,
                                                          self.bottomright, self.bottomleft], width=1)
        pygame.draw.rect(Camera.screen, "green", self.external_rect, width= 2)


    def collidepoint(self, point: Vector2):
        """基于仿射变换
            point 和 square 都转 -rotation
        """
        transed_point = self.center + (point - self.center).rotate(-self.rotation)

        return self.rect0.collidepoint(transed_point)

    def collidesquare(self, square):
        if (square.center - self.center).length() < (square.width + self.width)/2:
            return True
        elif (square.center - self.center).length() > (square.radius + self.radius):
            return False
        else:
            for point in [square.topleft, square.topright, square.bottomright, square.bottomleft]:
                if self.collidepoint(point):
                    return True
        return False


class Animation:
    def __init__(self, master: object, xmlpath: str = "./profile/anima.unknown.xml", **kwargs):
        self.master = master

        self.pausing = False
        self.state = "NORMAL"
        self.states: dict = {}
        self.statetime = 0 #milisecond
        self.playedframes = 0

        #图像

        self.scale = Vector2(1)
        self.scale_to_square_size = kwargs.get("scale_to_square_size", True)
        self.rotation = master.rotation



        #解析xml
        root = ET.parse(xmlpath).getroot()
        for state_element in root.findall("state"):
            self.states[state_element.get("name")]: dict = {"fps" : int(state_element.find("fps").text),
                                                      "image_cycle" : None}
            imagelist = []
            for frame in state_element.findall("frame"):
                imagelist.append(pygame.image.load(frame.get("src")).convert_alpha())
            self.states[state_element.get("name")]["image_cycle"] = cycle(imagelist)

        self.ori_image = next(self.cyclenow)

    @lru_cache(maxsize = 5)
    def _get_transformed_image(self, ori_image: pygame.Surface, scale: tuple, rotation: float):
        #辅助函数
        temp = ori_image
        temp = pygame.transform.rotate(temp, degrees(rotation))
        if self.scale_to_square_size:
            temp = pygame.transform.scale(temp, (self.master.rect.width * scale[0],self.master.rect.height * scale[1]))
        else:
            temp = pygame.transform.scale(temp, (temp.get_width() * scale[0],temp.get_height() * scale[1]))
        return temp

    def get_transformed_image(self):
        return self._get_transformed_image(self.ori_image, tuple(self.scale), self.rotation)

    @property
    def cyclenow(self):
        return self.states[self.state]["image_cycle"]

    @property
    def fpsnow(self):
        return self.states[self.state]["fps"]

    @property
    def image(self):
        return self.get_transformed_image()

    def change_state_to(self, newstate: str):
        self.state = newstate
        self.statetime = 0
        self.playedframes = 0

    def update(self, delta):
        self.rotation = self.master.rotation
        if not self.pausing:
            self.statetime += delta
            #如果下一帧
            if self.statetime // (1000 / self.fpsnow) > self.playedframes:
                self.playedframes += 1
                self.ori_image = next(self.cyclenow)


class SquareSprite(pygame.sprite.Sprite):
    """无父 方形精灵"""
    def __init__(self, square: Square, **kwargs):
        """__init__(square)

           __init__(square, xmlpath = '')"""
        super().__init__()
        self.square = square
        if kwargs.get("xmlpath") != None:
            self.anima = Animation(self, kwargs["xmlpath"])
        else:
            self.anima = Animation(self)

        self.state = NORMAL

        self.velocity = Vector2()


    @property
    def rotation(self):
        return self.square.rotation

    @rotation.setter
    def rotation(self, val):
        self.square.rotation = val

    @property
    def rect(self):
        """返回 外接矩形"""
        return self.square.external_rect

    def change_state_to(self, newstate):
        self.state = newstate
        self.anima.change_state_to(newstate)

    def _process(self, delta):
        pass

    def process(self, delta):
        self._process(delta)
        self.anima.update(delta)
        Camera.blit(self.anima.image, self.square.external_rect.topleft)

    def _physics_process(self, delta):
        pass

    def physics_process(self, delta):
        self._physics_process(delta)
        self.square.center += self.velocity * delta

    def update(self, delta):
        self.physics_process(delta)
        self.process(delta)






class Camera:
    pygame.display.init()
    screen_info = pygame.display.Info()
    WINSIZE = Vector2(screen_info.current_w, screen_info.current_h)

    offset = Vector2()
    target = None

    @classmethod
    def init(cls):
        cls.screen = pygame.display.set_mode(cls.WINSIZE)

    @classmethod
    def blit(cls, sur, pos):
        if cls.target != None:
            # cls.screen.blit()
            raise Exception("还没编")
        else:
            cls.screen.blit(sur, pos + cls.offset)

    @classmethod
    def campos(cls, worldpos):
        if cls.target != None:
            return worldpos - (cls.target.mass_center + cls.offset)
            raise Exception("乱编的")
        else:
            return worldpos - cls.offset

    @classmethod
    def worldpos(cls, campos):
        if cls.target != None:
            return campos + (cls.target.mass_center + cls.offset)
            raise Exception("乱编的")
        else:
            return campos + cls.offset


class Game:
    def __init__(self):
        pass

    @classmethod
    def init(cls):
        pygame.init()
        Camera.init()

        cls.clock = pygame.time.Clock()

    @classmethod
    def quit(cls):
        pygame.quit()
        sys.exit()

    

class BiosMgr:
    all_bullets = pygame.sprite.Group()
    all_enemies = pygame.sprite.Group()
